package TDengineDB

import (
	"errors"
	"fmt"
	"gitee.com/ling-bin/go-utils/mapstruct"
	"reflect"
)

//DataHandFn 数据处理方法
type DataHandFn func(key string,sVal interface{})(tVal interface{})

//ExecuteResult 执行失败结果
//{
//	"status":"error",
//	"code":897,
//	"desc":"Database already exists"
//}
//执行成功结果
//{
//	"status":"succ",
//	"head":["ts","current","voltage","phase","location","groupdid"],
//	"data":[["2020-09-09 17:16:47.332",10.20000,219,0.32000,"Beijing.Chaoyang",2]],
//	"rows":1
//}
/*
说明：

status: 告知操作结果是成功还是失败。
head: 表的定义，如果不返回结果集，则仅有一列“affected_rows”。（从 2.0.17 版本开始，建议不要依赖 head 返回值来判断数据列类型，而推荐使用 column_meta。在未来版本中，有可能会从返回值中去掉 head 这一项。）
column_meta: 从 2.0.17 版本开始，返回值中增加这一项来说明 data 里每一列的数据类型。具体每个列会用三个值来说明，分别为：列名、列类型、类型长度。例如["current",6,4]表示列名为“current”；列类型为 6，也即 float 类型；类型长度为 4，也即对应 4 个字节表示的 float。如果列类型为 binary 或 nchar，则类型长度表示该列最多可以保存的内容长度，而不是本次返回值中的具体数据长度。当列类型是 nchar 的时候，其类型长度表示可以保存的 unicode 字符数量，而不是 bytes。
data: 具体返回的数据，一行一行的呈现，如果不返回结果集，那么就仅有[[affected_rows]]。data 中每一行的数据列顺序，与 column_meta 中描述数据列的顺序完全一致。
rows: 表明总共多少行数据。
column_meta 中的列类型说明：
1：BOOL
2：TINYINT
3：SMALLINT
4：INT
5：BIGINT
6：FLOAT
7：DOUBLE
8：BINARY
9：TIMESTAMP
10：NCHAR
*/
type ExecuteResult struct {
	Status     string          `json:"status"`      //执行状态
	Head       []string        `json:"head"`        //查询的头部[列名]切片[从 2.0.17 版本开始可能去掉]
	ColumnMeta [][]interface{} `json:"column_meta"` //从 2.0.17 版本开始["current",6,4],列名为“current”；列类型为 6
	Data       [][]interface{} `json:"data"`        //数据二维数组切片
	Rows       int             `json:"rows"`        //影响行数
	Code       int             `json:"code"`        //错误码(864:表存在，897:数据库存在)
	Desc       string          `json:"desc"`        //错误描述
}

//是否成功
func (e *ExecuteResult) IsSucc() bool {
	return e.Status == "succ"
}

//ToSliceMap 获取执行结果转切片map
func (e *ExecuteResult) ToSliceMap() ([]map[string]interface{},error) {
	if !e.IsSucc() {
		return nil, errors.New(fmt.Sprint("状态：", e.Status, "  错误码:", e.Code, "  错误描述：", e.Desc))
	}
	// 2.0.17 版本
	v2_0_17 := len(e.Head) == 0
	result := make([]map[string]interface{}, 0, len(e.Data[0]))
	for _, rows := range e.Data {
		count := len(rows)
		row := make(map[string]interface{}, count)
		for i := 0; i < count; i++ {
			if v2_0_17 {
				row[e.ColumnMeta[i][0].(string)] = rows[i]
			} else {
				row[e.Head[i]] = rows[i]
			}
		}
		result = append(result, row)
	}
	return result, nil
}

//ToSlice 转换为切片对象
func (e *ExecuteResult) ToSlice(results interface{}) error {
	reflectRawSlice := reflect.TypeOf(results)
	rawKind := reflectRawSlice.Kind()
	rawElement := reflectRawSlice.Elem()
	if (rawKind == reflect.Ptr && rawElement.Kind() != reflect.Slice) ||
		(rawKind != reflect.Ptr && rawKind != reflect.Slice) {
		return fmt.Errorf("Incompatible Value, Looking For Slice : %v : %v", rawKind, rawElement.Kind())
	}
	// Create a slice large enough to decode all the values
	valSlice := reflect.MakeSlice(rawElement, len(e.Data), len(e.Data))
	// 2.0.17 版本
	v2_0_17 := len(e.Head) == 0
	for key, rows := range e.Data {
		count := len(rows)
		row := make(map[string]interface{}, count)
		for i := 0; i < count; i++ {
			if v2_0_17 {
				row[e.ColumnMeta[i][0].(string)] = rows[i]
			} else {
				row[e.Head[i]] = rows[i]
			}
		}
		sliceElementType := rawElement.Elem()
		if sliceElementType.Kind() != reflect.Ptr {
			// A slice of objects
			obj := reflect.New(rawElement.Elem())
			mapstruct.Decode(row, obj.Interface())
			indexVal := valSlice.Index(key)
			indexVal.Set(reflect.Indirect(obj))
		} else {
			// A slice of pointers
			obj := reflect.New(rawElement.Elem().Elem())
			mapstruct.Decode(row, obj.Interface())
			fmt.Println(obj.Interface())
			indexVal := valSlice.Index(key)
			indexVal.Set(obj)
		}
	}
	reflect.ValueOf(results).Elem().Set(valSlice)
	return nil
}